import { Scene, Math, Actions } from "phaser";
import foodImg from "../images/food.png";
import snakeImg from "../images/snake.png";

const UP = 0;
const DOWN = 1;
const LEFT = 2;
const RIGHT = 3;

let cursors: Phaser.Types.Input.Keyboard.CursorKeys;
// 食物
let food: Phaser.GameObjects.Image;
// 蛇
let snake: Phaser.GameObjects.Sprite[];

// 步长（蛇的每一个节点大小，一次移动距离）
let step = 16;
// 速度
let speed = 200;
// 最新一次移动时间
let moveTime = 0;
// 当前朝向
let direction = RIGHT;
// 下次移动的朝向
let nextDirection = RIGHT;
// 移动后抛下的尾部坐标（当吃到食物时，追加尾部使用）
let tailVector = new Math.Vector2();

// 最大横坐标和纵坐标
let maxX: number;
let maxY: number;

export class Main extends Scene {
    preload() {
        this.load.image("food", foodImg);
        this.load.image("snake", snakeImg);
    }
    create() {
        // 根据画布宽高和步长，计算最大横坐标和纵坐标
        maxX = (this.game.config.width as number) - step;
        maxY = (this.game.config.height as number) - step;

        // 创建蛇
        let pos = this.getRandomXY();
        snake = [this.add.sprite(pos.x, pos.y, "snake")];
        snake[0].setOrigin(0);

        // 创建食物
        pos = this.getRandomXY();
        food = this.add.image(pos.x, pos.y, "food");
        food.setOrigin(0);

        // 创建键盘事件
        cursors = this.input.keyboard.createCursorKeys();
    }
    update(time: number) {
        // 设置方向
        this.setDirection();

        // 当前时间必须大于上次移动时间 + 间隔
        if (time >= moveTime + speed) {
            moveTime = time;
            // 移动成功
            if (this.move()) {
                // 蛇头与食物重合
                if (snake[0].x == food.x && snake[0].y == food.y) {
                    this.eat();
                }
            }
        }
    }
    // 设置方向
    setDirection() {
        // 左键
        if (cursors.left.isDown && direction != RIGHT) {
            nextDirection = LEFT;
        }
        // 右键
        else if (cursors.right.isDown && direction != LEFT) {
            nextDirection = RIGHT;
        }
        // 上键
        else if (cursors.up.isDown && direction != DOWN) {
            nextDirection = UP;
        }
        // 下键
        else if (cursors.down.isDown && direction != UP) {
            nextDirection = DOWN;
        }
    }
    // 蛇移动
    move() {
        // 保存方向
        direction = nextDirection;

        // 当前蛇头坐标
        let headX = snake[0].x;
        let headY = snake[0].y;

        // 计算蛇头下一步坐标
        switch (direction) {
            case LEFT:
                headX = this.wrap(headX - step, 0, maxX);
                break;
            case RIGHT:
                headX = this.wrap(headX + step, 0, maxX);
                break;
            case UP:
                headY = this.wrap(headY - step, 0, maxY);
                break;
            case DOWN:
                headY = this.wrap(headY + step, 0, maxY);
                break;
        }
        // 蛇的每个节点坐标向前移动一位，保存最后抛出的坐标
        tailVector = Actions.ShiftPosition(snake, headX, headY, 1);

        // 判断蛇身和蛇头是否有重合
        let hitBody = Actions.GetFirst(snake, { x: headX, y: headY }, 1);

        // 游戏结束
        if (hitBody) {
            this.gameOver();
            return false;
        }
        return true;
    }
    // 蛇吃食物
    eat() {
        // 追加尾部
        let tail = this.add.sprite(tailVector.x, tailVector.y, "snake");
        tail.setOrigin(0);
        snake.push(tail);

        // 每吃5个，速度就会增加一点
        if (speed > 100 && snake.length % 5 == 0) {
            speed -= 10;
        }

        // 获取随机空位置
        let pos = this.getRandomXY();
        // 没有空位置了，游戏通关
        if (!pos) {
            this.gameWin();
        } else {
            food.setPosition(pos.x, pos.y);
        }
    }
    // 游戏失败
    gameOver() {
        this.sys.pause();
        alert("Game over!\n得分：" + snake.length);
    }
    // 游戏通关
    gameWin() {
        this.sys.pause();
        alert("You win!\n得分：" + snake.length);
    }
    // 坐标计算，用于蛇穿墙
    wrap(value: number, min: number, max: number): number {
        if (value < min) {
            return max;
        }
        if (value > max) {
            return min;
        }
        return value;
    }
    // 获取随机空坐标（排除已有的蛇和食物坐标）
    getRandomXY() {
        let position: Set<string> = new Set();

        // 获取所有坐标点
        for (let x = 0; x <= maxX; x += step) {
            for (let y = 0; y <= maxY; y += step) {
                position.add(`${x}|${y}`);
            }
        }
        // 排除蛇的坐标
        if (snake) {
            for (let item of snake) {
                position.delete(`${item.x}|${item.y}`);
            }
        }
        // 排除食物坐标
        if (food) {
            position.delete(`${food.x}|${food.y}`);
        }

        // 已经没有空坐标
        if (position.size == 0) {
            return null;
        }
        // 从剩余空坐标中取随机一个
        else {
            let [x, y] = Math.RND.pick([...position]).split("|");
            return {
                x: parseInt(x),
                y: parseInt(y),
            };
        }
    }
}
